
import os
import requests
import subprocess
from typing import Optional

class GitlabCloner:
    def __init__(self, gitlab_url: str, private_token: str, target_dir: str):
        self.gitlab_url = gitlab_url.rstrip('/')
        self.private_token = private_token
        self.target_dir = target_dir
        self.group_id = None
        self.headers = {'PRIVATE-TOKEN': private_token}
        
    def list_all_groups(self) -> Optional[int]:
        """列出所有可访问的组"""
        url = f"{self.gitlab_url}/api/v4/groups"
        params = {
            'per_page': 100,
            'all_available': True,
            'order_by': 'name',
            'sort': 'asc'
        }
        
        try:
            print("\n可用的组列表:")
            print("-" * 100)
            print(f"{'ID':<8} {'路径':<30} {'名称':<30} {'访问链接'}")
            print("-" * 100)
            
            page = 1
            while True:
                params['page'] = page
                response = requests.get(url, headers=self.headers, params=params)
                response.raise_for_status()
                groups = response.json()
                
                if not groups:
                    break
                
                for group in groups:
                    group_link = f"{self.gitlab_url}/{group['full_path']}"
                    print(f"{group['id']:<8} {group['path']:<30} {group['name']:<30} {group_link}")
                
                page += 1
            
            print("-" * 100)
            choice = input("\n请输入要克隆的组ID: ")
            return int(choice)
                
        except requests.exceptions.RequestException as e:
            print(f"获取组列表失败: {str(e)}")
            return None
        except ValueError:
            print("输入的ID无效")
            return None
            
    def get_projects(self, page: int = 1, per_page: int = 50) -> tuple:
        """获取项目列表"""
        url = f"{self.gitlab_url}/api/v4/groups/{self.group_id}/projects"
        params = {
            'page': page,
            'per_page': per_page,
            'order_by': 'name',
            'sort': 'asc',
            'include_subgroups': True
        }
        
        print(f"正在获取第 {page} 页项目...")
        response = requests.get(url, headers=self.headers, params=params)
        response.raise_for_status()
        
        total_pages = int(response.headers.get('X-Total-Pages', 1))
        total_items = int(response.headers.get('X-Total', 0))
        
        if page == 1:
            print(f"总共有 {total_items} 个项目，分 {total_pages} 页")
            
        projects = response.json()
        print(f"第 {page} 页获取到 {len(projects)} 个项目")
        return projects, total_pages

    def execute_git_command(self, command: list) -> bool:
        """执行git命令并显示原始进度"""
        try:
            result = subprocess.run(command, check=True)
            return result.returncode == 0
        except subprocess.CalledProcessError as e:
            print(f"错误: {str(e)}")
            return False
    
    def clone_project(self, project: dict, current: int, total: int) -> None:
        """克隆单个项目"""
        project_name = project['name']
        project_url = project['http_url_to_repo']
        project_url = project_url.replace('https://', f'https://oauth2:{self.private_token}@')
        project_path = os.path.join(self.target_dir, project_name)
        
        print(f"\n[{current}/{total}] 处理项目: {project_name}")
        
        if os.path.exists(project_path):
            try:
                # 检查是否有更新
                subprocess.run(['git', '-C', project_path, 'remote', 'update'], check=True)
                result = subprocess.run(
                    ['git', '-C', project_path, 'status', '-uno'],
                    capture_output=True,
                    text=True,
                    check=True
                )
                
                if 'Your branch is up to date' in result.stdout:
                    print(f"✓ 项目已是最新: {project_name}")
                    return
                    
                print(f"开始更新: {project_name}")
                success = self.execute_git_command(
                    ['git', '-C', project_path, 'pull', '--progress']
                )
                if success:
                    print(f"✓ 更新成功: {project_name}")
                else:
                    print(f"✗ 更新失败: {project_name}")
            except subprocess.CalledProcessError as e:
                print(f"✗ 更新失败: {project_name}, 错误: {str(e)}")
        else:
            print(f"开始克隆: {project_name}")
            success = self.execute_git_command(
                ['git', 'clone', '--progress', project_url, project_path]
            )
            if success:
                print(f"✓ 克隆成功: {project_name}")
            else:
                print(f"✗ 克隆失败: {project_name}")
    
    def clone_all_projects(self) -> None:
        """克隆所有项目"""
        # 首先让用户选择组
        self.group_id = self.list_all_groups()
        if not self.group_id:
            print("未选择有效的组ID，退出程序")
            return
            
        print(f"\n使用组ID: {self.group_id}")
        
        all_projects = []
        page = 1
        total_pages = None
        print("开始获取项目列表...")
        
        while True:
            try:
                projects, total_pages = self.get_projects(page=page)
                if not projects:
                    print("项目列表获取完成")
                    break
                
                all_projects.extend(projects)
                print(f"当前已获取 {len(all_projects)} 个项目")
                
                if page >= total_pages:
                    break
                    
                page += 1
                
            except requests.exceptions.RequestException as e:
                print(f"获取项目列表失败: {str(e)}")
                break
        
        total = len(all_projects)
        print(f"\n总共发现 {total} 个项目，开始处理...")
        
        for index, project in enumerate(all_projects, 1):
            self.clone_project(project, index, total)

def main():
    # 配置信息
    gitlab_url = ""  # GitLab 实例地址
    private_token = ""  # 你的访问令牌
    target_dir = "./backend"  # 克隆目标目录
    
    os.makedirs(target_dir, exist_ok=True)
    
    cloner = GitlabCloner(gitlab_url, private_token, target_dir)
    cloner.clone_all_projects()

if __name__ == "__main__":
    main()
